import json
import requests
import time
import abc
import logging

import xml.etree.cElementTree as ET


LOG = logging.getLogger(__name__)


def api_url(path):
    return 'https://qyapi.weixin.qq.com/cgi-bin/' + path


class WeiXinException(Exception):
    def __init__(self, code, message):
        self.code = code
        self.message = message

    def __str__(self):
        return "WEIXIN ERROR({0}: {1})".format(self.code, self.message)


def request(method, url, **kwargs):
    result_json = requests.request(method, url, **kwargs).json()
    if 'errcode' in result_json and result_json['errcode'] != 0:
        raise WeiXinException(result_json['errcode'], result_json['errmsg'])
    return result_json


class WeiXinClientFactory:
    def __init__(self, agent_id):
        self.agent_id = agent_id

    def create_by_corp(self, corp_id, secret):
        token = request('get', api_url("gettoken"), params={
            "corpid": corp_id,
            "corpsecret": secret
        })['access_token']
        return WeiXinClient(token, self.agent_id)


class WeiXinClient:
    def __init__(self, token, agent_id):
        self.token = token
        self.agent_id = agent_id

    def publish_message(self, message,
                        users='@all',
                        parties=None,
                        tags=None,
                        message_type='text',
                        safe=False):
        payload = json.dumps({
            "touser": users,
            "toparty": parties,
            "totag": tags,
            "msgtype": message_type,
            "agentid": self.agent_id,
            message_type: {
                "content": message,
            },
            "safe": 1 if safe else 0
        }, ensure_ascii=False)
        request('post', api_url('message/send'), params={
            "access_token": self.token
        }, data=payload)

    def list_users(self, depart_id):
        response = request('get', api_url('user/list'), params={
            'access_token': self.token,
            'department_id': depart_id,
            'fetch_child': 0,
            'status': 0
        })

        users = response['userlist']
        for user in users:
            extract_attributes(user)

        return users

    def get_user(self, user_id):
        user_json = request('get', api_url('user/list'), params={
            'access_token': self.token,
            'userid': user_id
        })
        extract_attributes(user_json)
        return user_json


def extract_attributes(user):
    attributes = dict()
    user['attributes'] = attributes
    attrs = user['extattr']['attrs']
    for attr in attrs:
        attributes[attr['name']] = attr['value']


_MSG_TYPE_TABLE = dict()


def weixin_message(name):
    def _wrapper(cls):
        _MSG_TYPE_TABLE[name] = cls
        return cls

    return _wrapper


def parse_xml_message(web_data):
    if len(web_data) == 0:
        raise Exception('content is empty')

    xml_data = ET.fromstring(web_data)
    msg_type = xml_data.find('MsgType').text
    creator = _MSG_TYPE_TABLE.get(msg_type, None)
    if creator:
        return creator(xml_data)
    else:
        raise Exception('unsupported message type: {}'.format(msg_type))


class Message(object):
    __metaclass__ = abc.ABCMeta

    def __init__(self, xml_data):
        self.xml_data = xml_data
        self.target_user = xml_data.find('ToUserName').text
        self.source_user = xml_data.find('FromUserName').text
        self.create_time = xml_data.find('CreateTime').text
        self.type = xml_data.find('MsgType').text
        self.id = xml_data.find('MsgId').text

    def field(self, name):
        f = self.xml_data.find(name)
        if f is not None:
            return f.text
        else:
            return None


@weixin_message('text')
class TextMessage(Message):
    def __init__(self, xml_data):
        super(TextMessage, self).__init__(xml_data)
        self.content = self.field('Content')


class MediaMessage(Message):
    __metaclass__ = abc.ABCMeta

    def __init__(self, xml_data):
        super(MediaMessage, self).__init__(xml_data)
        self.media_id = self.field('MediaId')


@weixin_message('image')
class ImageMessage(MediaMessage):
    def __init__(self, xml_data):
        super(ImageMessage, self).__init__(xml_data)
        self.picture_url = self.field('PicUrl')


@weixin_message('voice')
class VoiceMessage(MediaMessage):
    def __init__(self, xml_data):
        super(VoiceMessage, self).__init__(xml_data)
        self.format = self.field('Format')


@weixin_message('video')
class VideoMessage(MediaMessage):
    def __init__(self, xml_data):
        super(VideoMessage, self).__init__(xml_data)
        self.thumb_media_id = self.field('ThumbMediaId')


@weixin_message('shortvideo')
class ShortVideoMessage(MediaMessage):
    def __init__(self, xml_data):
        super(ShortVideoMessage, self).__init__(xml_data)
        self.thumb_media_id = self.field('ThumbMediaId')


@weixin_message('location')
class LocationMessage(Message):
    def __init__(self, xml_data):
        super(LocationMessage, self).__init__(xml_data)
        self.x = self.field('Location_X')
        self.y = self.field('Location_Y')
        self.label = self.field('Label')
        self.scale = self.field('Scale')


@weixin_message('link')
class LinkMessage(Message):
    def __init__(self, xml_data):
        super(LinkMessage, self).__init__(xml_data)
        self.url = self.field('Url')
        self.title = self.field('Title')
        self.description = self.field('Description')


@weixin_message('event')
class EventMessage(Message):
    def __init__(self, xml_data):
        super(EventMessage, self).__init__(xml_data)
        self.event_type = self.field('Event')
        self.latitude = self.field('Latitude')
        self.longitude = self.field('Longitude')
        self.precision = self.field('Precision')
        self.event_key = self.field('EventKey')
        self.ticket = self.field('Ticket')


class Reply(object):
    def dumps(self):
        return u"success"


class TextReply(Reply):
    def __init__(self, target_user, source_user, content):
        self.__dict = dict()
        self.__dict['ToUserName'] = target_user
        self.__dict['FromUserName'] = source_user
        self.__dict['CreateTime'] = int(time.time())
        self.__dict['Content'] = content

    def dumps(self):
        return u"""
        <xml>
        <ToUserName><![CDATA[{ToUserName}]]></ToUserName>
        <FromUserName><![CDATA[{FromUserName}]]></FromUserName>
        <CreateTime>{CreateTime}</CreateTime>
        <MsgType><![CDATA[text]]></MsgType>
        <Content><![CDATA[{Content}]]></Content>
        </xml>
        """.format(**self.__dict)


class ImageReply(Reply):
    def __init__(self, target_user, source_user, mediaId):
        self.__dict = dict()
        self.__dict['ToUserName'] = target_user
        self.__dict['FromUserName'] = source_user
        self.__dict['CreateTime'] = int(time.time())
        self.__dict['MediaId'] = mediaId

    def dumps(self):
        return u"""
        <xml>
        <ToUserName><![CDATA[{ToUserName}]]></ToUserName>
        <FromUserName><![CDATA[{FromUserName}]]></FromUserName>
        <CreateTime>{CreateTime}</CreateTime>
        <MsgType><![CDATA[image]]></MsgType>
        <Image>
        <MediaId><![CDATA[{MediaId}]]></MediaId>
        </Image>
        </xml>
        """.format(**self.__dict)
